﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Timers;
using System.Windows.Media;
using System.Windows.Threading;

namespace System.Windows.Controls
{
    /// <summary>
    /// A WPF Analog/circular/Radial Gauge
    /// Includes Ranges/ Scale and multiple captions Caption
    /// </summary>
    public partial class AnalogGauge
    {
        public AnalogGauge()
        {
            InitializeComponent();
            //DataContext = this;
            _grid.DataContext = this;


            Scale = new Scale { Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 2 };
            Scale.PropertyChanged += ScalePropertyChanged;
            Scale.Radius = (int)Width / 2 - 3;
            AddLogicalChild(Scale);
            BaseBackground = new SolidColorBrush(Colors.Black);
            BezelThickness = 3;

            Radius = (int)Width / 2;

            CenterPoint = new Point(Width / 2, Height / 2);
            Center = new Point(0, 0);
            StartAngle = 225;
            Sweep = 270;
            BezelBackground = new SolidColorBrush(Colors.DarkGray);
            BezelCornerRadius = Radius;
            NeedleBaseRadius = 30;
            NeedleBase = new SolidColorBrush(Colors.DarkGray);
            ScaleBackground = new SolidColorBrush(Colors.AntiqueWhite);
            Captions = new ObservableCollection<GaugeCaption>();
            Captions.CollectionChanged += CaptionsCollectionChanged;
            RangeDefinitions = new ObservableCollection<RangeDefinition>();
            RangeDefinitions.CollectionChanged += RangeDefinitionsCollectionChanged;
            Ranges = new ObservableCollection<Range>();

            ScaleBeginOuter = GetCoordinate(CenterPoint, Scale.Radius, StartAngle);
            ScaleEndOuter = GetCoordinate(CenterPoint, Scale.Radius, StartAngle - Sweep);

            _timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
        }

        
              
        /// <summary>
        /// The Value to be displayed on the Gauge
        /// </summary>
        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        /// <summary>
        /// 
        /// </summary>
        public double PrivateValue
        {
            get { return (double)GetValue(PrivateValueProperty); }
            set { SetValue(PrivateValueProperty, value); }
        }
        /// <summary>
        /// The maximum scale value
        /// </summary>
        public int MaxValue
        {
            get { return (int)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }
        /// <summary>
        /// The minimum scale value
        /// </summary>
        public int MinValue
        {
            get { return (int)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }
        /// <summary>
        /// The Scale background brush (color)
        /// </summary>
        public Brush ScaleBackground
        {
            get { return (Brush)GetValue(ScaleBackgroundProperty); }
            set { SetValue(ScaleBackgroundProperty, value); }
        }
        /// <summary>
        /// The thickness of the gauge bezel
        /// </summary>
        public int BezelThickness
        {
            get { return (int)GetValue(GaugeThicknessProperty); }
            set { SetValue(GaugeThicknessProperty, value); }
        }
        /// <summary>
        /// The gauge base color ( brush)
        /// </summary>
        public Brush BaseBackground
        {
            get { return (Brush)GetValue(BaseBackgroundProperty); }
            set { SetValue(BaseBackgroundProperty, value); }
        }
        /// <summary>
        /// The Bezel color
        /// </summary>
        public Brush BezelBackground
        {
            get { return (Brush)GetValue(GaugeBackgroundProperty); }
            set { SetValue(GaugeBackgroundProperty, value); }
        }
        /// <summary>
        /// The curvature of the gauge. For a round gauge make this half the width/height
        /// of the gauge
        /// </summary>
        public int BezelCornerRadius
        {
            get { return (int)GetValue(BezelCornerRadiusProperty); }
            set { SetValue(BezelCornerRadiusProperty, value); }
        }
        /// <summary>
        /// The center point of the gauge around which the display elements pivot
        /// </summary>
        public Point CenterPoint
        {
            get { return (Point)GetValue(CenterPointProperty); }
            set { SetValue(CenterPointProperty, value); }
        }

        
        public Point Center
        {
            get { return (Point)GetValue(CenterProperty); }
            set { SetValue(CenterProperty, value); }
        }
        public static DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(AnalogGauge), new PropertyMetadata(new PropertyChangedCallback(OnCenterChanged)));

        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            var point = (Point)e.NewValue;
            if(point.X > 0 && point.Y > 0)
            {
                gauge.CenterPoint = point;
            }
            else
            {
                gauge.CenterPoint = new Point(gauge.Width/2,gauge.Height/2); 
            }
        }
        /// <summary>
        /// The angle (0-360) at which the scale display starts
        /// </summary>
        public int StartAngle
        {
            get { return (int)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }
        /// <summary>
        /// The angle (0-360) over which the scale will span
        /// </summary>
        public int Sweep
        {
            get { return (int)GetValue(SweepProperty); }
            set { SetValue(SweepProperty, value); }
        }
        /// <summary>
        /// The color ( brush) of the needle/indicator
        /// </summary>
        public Brush NeedleBrush
        {
            get { return (Brush)GetValue(NeedleBrushProperty); }
            set { SetValue(NeedleBrushProperty, value); }
        }
        /// <summary>
        /// The thickness of the needle
        /// </summary>
        public int NeedleThickness
        {
            get { return (int)GetValue(NeedleThicknessProperty); }
            set { SetValue(NeedleThicknessProperty, value); }
        }
        /// <summary>
        /// The length of the needle
        /// </summary>
        public int NeedleRadius
        {
            get { return (int)GetValue(NeedleRadiusProperty); }
            set { SetValue(NeedleRadiusProperty, value); }
        }
        /// <summary>
        /// A collection of Captions
        /// </summary>
        public ObservableCollection<GaugeCaption> Captions
        {
            get { return (ObservableCollection<GaugeCaption>)GetValue(CaptionsProperty); }
            set { SetValue(CaptionsProperty, value); }
        }
        /// <summary>
        /// A collection of Range definitiions
        /// </summary>
        public ObservableCollection<RangeDefinition> RangeDefinitions
        {
            get { return (ObservableCollection<RangeDefinition>)GetValue(RangeDefinitionsProperty); }
            set { SetValue(RangeDefinitionsProperty, value); }
        }
        /// <summary>
        /// The color of needle base
        /// </summary>
        public Brush NeedleBase
        {
            get { return (Brush)GetValue(NeedleBaseProperty); }
            set { SetValue(NeedleBaseProperty, value); }
        }
        /// <summary>
        /// The radius of the needle base
        /// </summary>
        public int NeedleBaseRadius
        {
            get { return (int)GetValue(NeedleBaseRadiusProperty); }
            set { SetValue(NeedleBaseRadiusProperty, value); }
        }
        /// <summary>
        /// The size of the scale background
        /// </summary>
        public int ScaleBackgroundThickness
        {
            get { return (int)GetValue(ScaleBackgroundThicknessProperty); }
            set { SetValue(ScaleBackgroundThicknessProperty, value); }
        }
        /// <summary>
        /// The scale definition
        /// </summary>
        public Scale Scale
        {
            get { return (Scale)GetValue(ScaleProperty); }
            set { SetValue(ScaleProperty, value); }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (Scale != null)
            {
                if (e.Property.Name == "Width")
                {
                    double width = (double)e.NewValue;
                    Radius = (int)Math.Min(width, this.Height) / 2;
                    if (Center.X > 0 && Center.Y > 0)
                        CenterPoint = Center;
                    else
                        CenterPoint = new Point(width / 2, this.Height / 2);
                    UpdateArcPoints(this);
                }
                if (e.Property.Name == "Height")
                {
                    double height = (double)e.NewValue;
                    Radius = (int)Math.Min(this.Width, height) / 2;
                    if (Center.X > 0 && Center.Y > 0)
                        CenterPoint = Center;
                    else
                        CenterPoint = new Point(this.Width / 2,height / 2);
                    UpdateArcPoints(this);
                }
            }
        }
        
        #region Dependency Properties

        
        private static readonly DependencyProperty NeedleEndPointProperty = DependencyProperty.Register(
            "NeedleEndPoint",
            typeof(Point),
            typeof(AnalogGauge), null);


        private static readonly DependencyProperty ScaleDataProperty = DependencyProperty.Register("ScaleData",
                                                                                                   typeof(string),
                                                                                                   typeof(AnalogGauge),
                                                                                                   null);


        private static readonly DependencyProperty ScaleBeginProperty = DependencyProperty.Register("ScaleBegin",
                                                                                                    typeof(Point),
                                                                                                    typeof(AnalogGauge),
                                                                                                    null);

        private static readonly DependencyProperty ScaleBeginOuterProperty =
            DependencyProperty.Register("ScaleBeginOuter", typeof(Point), typeof(AnalogGauge), null);


        private static readonly DependencyProperty EndProperty = DependencyProperty.Register("ScaleEnd", typeof(Point),
                                                                                             typeof(AnalogGauge), null);

        private static readonly DependencyProperty ScaleEndOuterProperty = DependencyProperty.Register("ScaleEndOuter",
                                                                                                       typeof(Point),
                                                                                                       typeof(
                                                                                                           AnalogGauge),
                                                                                                       null);

        private static readonly DependencyProperty ScaleBackgroundThicknessProperty =
            DependencyProperty.Register("ScaleBackgroundThickness", typeof(int), typeof(AnalogGauge), null);


        private static readonly DependencyProperty ScaleBackgroundSizeProperty = DependencyProperty.Register(
            "ScaleSize",
            typeof(Size),
            typeof(AnalogGauge),
            null);


        private static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(Scale),
                                                                                               typeof(AnalogGauge),
                                                                                               new PropertyMetadata(OnScaleChanged));

        private static readonly DependencyProperty RangesProperty = DependencyProperty.Register("Ranges",
                                                                                                typeof(
                                                                                                    ObservableCollection
                                                                                                    <Range>),
                                                                                                typeof(AnalogGauge),
                                                                                                null);

        
        
        private readonly static DependencyProperty PrivateValueProperty = DependencyProperty.Register("PrivateValue", typeof(double), typeof(AnalogGauge), new PropertyMetadata(new PropertyChangedCallback(OnPrivateValueChanged)));

        private static void OnPrivateValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // insert your code here
        }

        
        public int Response
        {
            get { return (int)GetValue(ResponseProperty); }
            set { SetValue(ResponseProperty, value); }
        }
        private readonly static DependencyProperty ResponseProperty = DependencyProperty.Register("Response", typeof(int), typeof(AnalogGauge), new PropertyMetadata(100,new PropertyChangedCallback(OnResponseChanged)));

        private static void OnResponseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AnalogGauge gauge = (AnalogGauge)d;
            int response = (int)e.NewValue;
            if(response == 0)
            {
                gauge._timer.Enabled = false;
            }
            else
            {
                gauge._timer.Interval = 100;
                gauge._timer.Enabled = true;
            }
        }

        private static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof (int),
                                                                                               typeof (AnalogGauge),
                                                                                               new PropertyMetadata(50,
                                                                                                                    OnValueChanged,
                                                                                                                    OnCoerceValue));

        private static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue",
                                                                                                  typeof (int),
                                                                                                  typeof (AnalogGauge),
                                                                                                  new PropertyMetadata(
                                                                                                      100,
                                                                                                      OnMaxValueChanged,
                                                                                                      OnCoerceMaxValue));

        private static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof (int),
                                                                                        typeof (AnalogGauge),
                                                                                        new PropertyMetadata(0,
                                                                                                             OnMinValueChanged,
                                                                                                             OnCoerceMinValue));

        private static readonly DependencyProperty ScaleBackgroundProperty =
            DependencyProperty.Register("ScaleBackground", typeof (Brush), typeof (AnalogGauge), null);

        private static readonly DependencyProperty GaugeThicknessProperty = DependencyProperty.Register(
            "BezelThickness", typeof (int), typeof (AnalogGauge), new PropertyMetadata(GaugeBorderChangedCallback));

        private static readonly DependencyProperty BaseBackgroundProperty = DependencyProperty.Register(
            "BaseBackground",
            typeof (Brush),
            typeof (AnalogGauge), null);

        private static readonly DependencyProperty GaugeBackgroundProperty =
            DependencyProperty.Register("BezelBackground", typeof (Brush), typeof (AnalogGauge), null);

        private static readonly DependencyProperty BezelCornerRadiusProperty =
            DependencyProperty.Register("BezelCornerRadius", typeof (int), typeof (AnalogGauge),
                                        new PropertyMetadata(OnBezelCornerRadiusChanged));

        private static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register("CenterPoint", typeof (Point),
                                                                                                typeof (AnalogGauge),
                                                                                                new PropertyMetadata(
                                                                                                    CenterPointChangedCallback));

        private static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof (int),
                                                                                                typeof (AnalogGauge),
                                                                                                new PropertyMetadata(
                                                                                                    RadiusChangedCallback));

        private static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register("StartAngle",
                                                                                                    typeof (int),
                                                                                                    typeof (AnalogGauge),
                                                                                                    new PropertyMetadata
                                                                                                        (StartAngleChangedCallback));

        private static readonly DependencyProperty SweepProperty = DependencyProperty.Register("Sweep", typeof (int),
                                                                                               typeof (AnalogGauge),
                                                                                               new PropertyMetadata(
                                                                                                   SweepChangedCallback));

        private static readonly DependencyProperty IsLargeArcProperty = DependencyProperty.Register("IsLargeArc",
                                                                                                    typeof (int),
                                                                                                    typeof (AnalogGauge),
                                                                                                    new PropertyMetadata
                                                                                                        (OnIsLargeArcChanged));

        private static readonly DependencyProperty NeedleBrushProperty = DependencyProperty.Register("NeedleBrush",
                                                                                                     typeof (Brush),
                                                                                                     typeof (AnalogGauge
                                                                                                         ),
                                                                                                     new PropertyMetadata
                                                                                                         (new SolidColorBrush
                                                                                                              (Colors.
                                                                                                                   Firebrick)));

        private static readonly DependencyProperty NeedleThicknessProperty =
            DependencyProperty.Register("NeedleThickness", typeof (int), typeof (AnalogGauge), new PropertyMetadata(5));

        private static readonly DependencyProperty NeedleRadiusProperty = DependencyProperty.Register("NeedleRadius", typeof (int),
                                                                                            typeof (AnalogGauge),
                                                                                            new PropertyMetadata(100,
                                                                                                                 OnNeedleRadiusChanged));

        private static readonly DependencyProperty CaptionsProperty = DependencyProperty.Register("Captions",
                                                                                        typeof (
                                                                                            ObservableCollection
                                                                                            <GaugeCaption>),
                                                                                        typeof (AnalogGauge), null);

        private static readonly DependencyProperty RangeDefinitionsProperty = DependencyProperty.Register("RangeDefinitions",
                                                                                                typeof (
                                                                                                    ObservableCollection
                                                                                                    <RangeDefinition>),
                                                                                                typeof (AnalogGauge),
                                                                                                new PropertyMetadata(
                                                                                                    OnRangeDefinitionsChanged));

        private static readonly DependencyProperty NeedleBaseProperty = DependencyProperty.Register("NeedleBase",
                                                                                                    typeof (Brush),
                                                                                                    typeof (AnalogGauge),
                                                                                                    null);

        private static readonly DependencyProperty NeedleBaseRadiusProperty =
            DependencyProperty.Register("NeedleBaseRadius", typeof (int), typeof (AnalogGauge),
                                        new FrameworkPropertyMetadata(30, OnNeedleBaseRadiusPropertyChanged));

        #endregion

        

        private static object OnCoerceValue(DependencyObject d, object value)
        {
            var gauge = (AnalogGauge) d;
            var v = (int) value;
            if (v > gauge.MaxValue) v = gauge.MaxValue;
            if (v < gauge.MinValue) v = gauge.MinValue;
            return v;
        }

        private static void OnMaxValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            gauge.CoerceValue(MinValueProperty);
            UpdateNeedleEnd(gauge);
            gauge.Scale.UpdateLabels(gauge.CenterPoint, gauge.StartAngle, gauge.Sweep, gauge.MinValue, gauge.MaxValue);
        }

        private static object OnCoerceMaxValue(DependencyObject d, object value)
        {
            var gauge = (AnalogGauge) d;
            var v = (int) value;
            if (v < gauge.MinValue) v = gauge.MinValue;
            return v;
        }

        private static void OnMinValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            gauge.CoerceValue(MaxValueProperty);
            UpdateNeedleEnd(gauge);
            gauge.Scale.UpdateLabels(gauge.CenterPoint, gauge.StartAngle, gauge.Sweep, gauge.MinValue, gauge.MaxValue);
        }

        private static object OnCoerceMinValue(DependencyObject d, object value)
        {
            var gauge = (AnalogGauge) d;
            var v = (int) value;
            if (v > gauge.MaxValue) v = gauge.MaxValue;
            return v;
        }

        private static void GaugeBorderChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateArcPoints(gauge);
        }

        private static void OnBezelCornerRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        private static void CenterPointChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateArcPoints(gauge);
        }

        private static void RadiusChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateArcPoints(gauge);
        }

        private static void StartAngleChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateArcPoints(gauge);
        }

        private static void SweepChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            gauge.IsLargeArc = gauge.Sweep > 180 ? 1 : 0;
            UpdateArcPoints(gauge);
        }

        private static void OnIsLargeArcChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }

        private static void OnRangeDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge)d;
            if (e.OldValue != null)
            {
                var collection = (ObservableCollection<RangeDefinition>) e.OldValue;
                collection.CollectionChanged -= gauge.RangeDefinitionsCollectionChanged;
            }
            gauge.RangeDefinitions.CollectionChanged += gauge.RangeDefinitionsCollectionChanged;
        }

        private static void OnNeedleRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateNeedleEnd(gauge);
        }

        private static void OnNeedleBaseRadiusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            UpdateArcPoints(gauge);
        }

        private static Point GetCoordinate(Point center, int radius, double angle)
        {
            double radians = Math.PI*angle/180;

            return new Point(center.X + radius*(float) Math.Cos(radians),
                             center.Y - radius*(float) Math.Sin(radians));
        }

        private void UserControlLoaded(object sender, RoutedEventArgs e)
        {
            Radius = (int) Width/2;
            InvalidateVisual();
        }

        private static void UpdateArcPoints(AnalogGauge gauge)
        {
            gauge.ScaleBegin = GetCoordinate(gauge.CenterPoint, gauge.Radius/2, gauge.StartAngle);
            gauge.ScaleEnd = GetCoordinate(gauge.CenterPoint, gauge.Radius/2, gauge.StartAngle - gauge.Sweep);
            gauge.ScaleBackgroundThickness = gauge.Radius - (gauge.BezelThickness*2);
            UpdateScale(gauge);
            gauge.ScaleBackgroundSize = new Size(width: gauge.Radius/2, height: gauge.Radius/2);
            gauge.ScaleData = string.Format("M {0} A {1} 0 {2} 1 {3}", gauge.ScaleBegin, gauge.ScaleBackgroundSize,
                                            gauge.IsLargeArc, gauge.ScaleEnd);
            UpdateNeedleEnd(gauge);
            UpdateRanges(gauge);
        }

        private static void UpdateNeedleEnd(AnalogGauge gauge)
        {
            double angle = gauge.StartAngle - (int) (gauge.PrivateValue/(float) (gauge.MaxValue - gauge.MinValue)*gauge.Sweep);
            gauge.NeedleEndPoint = GetCoordinate(gauge.CenterPoint, gauge.NeedleRadius, angle);
        }

        private static void UpdateRanges(AnalogGauge gauge)
        {
            if (gauge.Ranges != null)
            {
                gauge.Ranges.Clear();
                List<Range> list = gauge.RangeDefinitions.ToList().ConvertAll(
                    def =>
                    new Range(def, gauge.CenterPoint, gauge.StartAngle, gauge.Sweep, gauge.Radius/2,
                              gauge.ScaleBackgroundSize));
                gauge.Ranges = new ObservableCollection<Range>(list);
            }
        }

        private void ScalePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            UpdateScale(this);
        }

        private void CaptionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (GaugeCaption def in e.NewItems)
                {
                    
                    AddLogicalChild(def);
                }
            }
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (GaugeCaption def in e.OldItems)
                {
                    RemoveLogicalChild(def);
                }
            }
        }

        private void RangeDefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (RangeDefinition def in e.NewItems)
                {
                    def.PropertyChanged += DefPropertyChanged;
                    AddLogicalChild(def);
                }
            }
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (RangeDefinition def in e.OldItems)
                {
                    def.PropertyChanged -= DefPropertyChanged;
                    RemoveLogicalChild(def);
                }
            }
            UpdateRanges(this);
        }

        private void DefPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            UpdateRanges(this);
        }
        


        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge)d;
            gauge.CoerceValue(MaxValueProperty);
            gauge.CoerceValue(MinValueProperty);
            if(!gauge._timer.Enabled)
            {
                gauge.PrivateValue = gauge.Value;
                UpdateNeedleEnd(gauge);
            }
            
           
        }
        
        private Point NeedleEndPoint
        {
            get { return (Point) GetValue(NeedleEndPointProperty); }
            set { SetValue(NeedleEndPointProperty, value); }
        }

        private string ScaleData
        {
            get { return (string) GetValue(ScaleDataProperty); }
            set { SetValue(ScaleDataProperty, value); }
        }
        
        private Point ScaleEnd
        {
            get { return (Point)GetValue(EndProperty); }
            set { SetValue(EndProperty, value); }
        }

        private Point ScaleEndOuter
        {
            get { return (Point)GetValue(ScaleEndOuterProperty); }
            set { SetValue(ScaleEndOuterProperty, value); }
        }

        private Size ScaleBackgroundSize
        {
            get { return (Size)GetValue(ScaleBackgroundSizeProperty); }
            set { SetValue(ScaleBackgroundSizeProperty, value); }
        }

        private ObservableCollection<Range> Ranges
        {
            get { return (ObservableCollection<Range>) GetValue(RangesProperty); }
            set { SetValue(RangesProperty, value); }
        }

        private Point ScaleBegin
        {
            get { return (Point)GetValue(ScaleBeginProperty); }
            set { SetValue(ScaleBeginProperty, value); }
        }

        private Point ScaleBeginOuter
        {
            get { return (Point)GetValue(ScaleBeginOuterProperty); }
            set { SetValue(ScaleBeginOuterProperty, value); }
        }
        
        private int IsLargeArc
        {
            get { return (int)GetValue(IsLargeArcProperty); }
            set { SetValue(IsLargeArcProperty, value); }
        }

        private int Radius
        {
            get { return (int)GetValue(RadiusProperty); }
            set { SetValue(RadiusProperty, value); }
        }

       
        private static void OnScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = (AnalogGauge) d;
            var scale = (Scale) e.OldValue;
            if (scale != null)
            {
                scale.PropertyChanged -= gauge.ScalePropertyChanged;
                gauge.RemoveLogicalChild(scale);
            }
            scale = (Scale) e.NewValue;
            scale.PropertyChanged += gauge.ScalePropertyChanged;
            gauge.AddLogicalChild(scale);
            UpdateScale(gauge);
        }

        private static void UpdateScale(AnalogGauge gauge)
        {
            gauge.ScaleBeginOuter = GetCoordinate(gauge.CenterPoint, gauge.Scale.Radius, gauge.StartAngle);
            gauge.ScaleEndOuter = GetCoordinate(gauge.CenterPoint, gauge.Scale.Radius, gauge.StartAngle - gauge.Sweep);
            gauge.Scale.UpdateLabels(gauge.CenterPoint, gauge.StartAngle, gauge.Sweep, gauge.MinValue, gauge.MaxValue);
        }

        void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
                                                                             {
                                                                                 _queue.Enqueue(Value);
                                                                                 if (_queue.Count > Response/5)
                                                                                 {
                                                                                     System.Diagnostics.Debug.WriteLine(
                                                                                         string.Format("{0}",_queue.Average()));
                                                                                     double d;
                                                                                     _queue.TryDequeue(out d);

                                                                                 }
                                                                                 if (_queue.Count > 0)
                                                                                 {
                                                                                     PrivateValue = _queue.Average();
                                                                                     UpdateNeedleEnd(this);
                                                                                 }
                                                                             }));

        }

        private readonly System.Collections.Concurrent.ConcurrentQueue<double> _queue = new ConcurrentQueue<double>(); 
        private readonly Timers.Timer _timer = new Timer();
        
    }
}